spring data jpa 实现多条件复杂查询及多表联查 - maybe的博客 - CSDN博客

创建时间:2019/3/5 11:34
来源:https://blog.csdn.net/qq_34083066/article/details/80733512


spring data jpa 实现多条件复杂查询及多表联查

2018年06月19日 16:32:29 maybe宸 阅读数:12013
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34083066/article/details/80733512

最近发现JPA在处理单表时,很方便,但多表就比较复杂了。今天写一下端午这两天琢磨的多条件查询,还有多表联查。

文章比较长,大部分都是代码,不愿意看的代码copy下去,直接可以实现;想交流的可以看完,然后留言交流。

maven依赖啊,配置,继承写法等知识点不展开说了,之前写过一篇文章:

spring boot 配置及使用 spring data jpa

这里说一下更新的地方:

JPA的配置

######################################################
###spring data JPA配置
######################################################
#指定JPA的DB
spring.jpa.database=MYSQL
#是否显示SQL
spring.jpa.show-sql=true
#执行DDL语句时,是创建create,创建删除create-drop,更新update
spring.jpa.hibernate.ddl-auto=update
#命名策略:当创建了entity,会在DB中创建一个表结构
#这个是驼峰命名法,遇到大写加下划线
#spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
#这个是默认写法,以属性名命名
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#hibernate配置DB方言
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

说一点,命名策略的配置更新了,以前是:

org.hibernate.cfg.DefaultNamingStrategy

org.hibernate.cfg.ImprovedNamingStrategy

但我发现配置了之后无论使用哪种都是带下划线的,所以查找了一下资料,发现现在使用上面的两个配置:

PhysicalNamingStrategyStandardImpl  默认以属性名作为字段名;

SpringPhysicalNamingStrategy 以驼峰法拆分加下划线为字段名。

其他注解几乎都写明白了,不是重点不展开赘述了。


然后上实例

系统分4层:entity,repository,service,controller

模拟:一个用户可以有多个地址,但是一条地址记录,只能对应一个用户。

entity:

两个实体webuser和address

因为两个实体有共性,都需要主键,创建时间,销毁时间.....所以抽出来单独写一个类。

entity父类:

  1. 1
    package com.wm.springboot.base;
  2. 2
  3. 3
    import java.util.Date;
  4. 4
  5. 5
    import javax.persistence.Column;
  6. 6
    import javax.persistence.GeneratedValue;
  7. 7
    import javax.persistence.GenerationType;
  8. 8
    import javax.persistence.Id;
  9. 9
    import javax.persistence.MappedSuperclass;
  10. 10
    import javax.persistence.Transient;
  11. 11
    import javax.persistence.Version;
  12. 12
  13. 13
    import com.alibaba.fastjson.annotation.JSONField;
  14. 14
  15. 15
    import lombok.Getter;
  16. 16
    import lombok.Setter;
  17. 17
  18. 18
    @MappedSuperclass //表明这是父类,可以将属性映射到子类中使用JPA生成表
  19. 19
    public abstract class BaseEntity extends BaseClass {
  20. 20
  21. 21
    @JSONField(ordinal=1) @Getter @Setter
  22. 22
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
  23. 23
    @Column(name="id",columnDefinition="int(30) comment '无意义自增主键'")
  24. 24
    protected Integer id; //无意义自增主键
  25. 25
  26. 26
  27. 27
    @JSONField(ordinal=2,format="yyyy-MM-dd HH:mm:ss") @Getter @Setter
  28. 28
    @Column(name="createTime",columnDefinition="DATETIME comment '创建时间'")
  29. 29
    protected Date createTime; //创建时间
  30. 30
  31. 31
    @JSONField(ordinal=3,format="yyyy-MM-dd HH:mm:ss") @Getter @Setter
  32. 32
    @Column(name="destroyTime",columnDefinition="DATETIME comment '销毁时间'")
  33. 33
    protected Date destroyTime; //销毁时间
  34. 34
  35. 35
  36. 36
    @JSONField(ordinal=4) @Getter @Setter
  37. 37
    @Version @Column(name="version",nullable=false,columnDefinition="int(20) comment '版本号'")
  38. 38
    protected Integer version;
  39. 39
  40. 40
    @JSONField(ordinal=5) @Getter @Setter
  41. 41
    @Column(length=1,name="isValid",nullable=false,columnDefinition="int(1) comment '是否启用,1:启用 0:不启用'")
  42. 42
    protected Integer isValid; //是否启用
  43. 43
  44. 44
    @Transient
  45. 45
    @JSONField(ordinal=5) @Getter @Setter
  46. 46
    protected String createTimeStart; //创建时间的开始点
  47. 47
  48. 48
    @Transient
  49. 49
    @JSONField(ordinal=6) @Getter @Setter
  50. 50
    protected String createTimeEnd; //创建时间的结束点
  51. 51
  52. 52
    }

1,由于是父类,所以不需要单独实例化,所以写成抽象类。

2,BaseClass不展开了,里面是国际化;这里只从baseEntity展开。

3,使用@MappedSuperclass注解,让子类在JPA生成表时可以使用父类继承来的属性。

4,@Getter @Setter 使用了插件lombok,自动生成getset方法,非常好用。多说一句:有人觉得这个东西改变了代码的写法,造成不好影响,我觉得目前使用来看,没给我造成什么不好影响,反而提高了我的效率。

5,@JSONField(ordinal=1) 继承了fastjson带的注解,这里仅做排序使用。其实可以不用写,我写习惯了。

6,@Id, 这个是生成表时的主键,按照数据库设计原则,主键应该是无意义自增主键。所以我觉得可以抽象出来放到父类;使每个表的主键都叫ID也不是什么问题;

7,@GeneratedValue(strategy=GenerationType.AUTO)  主键生成策略,自增;

8,@Column:网上一查一大堆。不过我的用法跟网上不太一样。简单说一下吧。

    a),name,映射到表时的字段名,这个和上面讲的命名策略相关。

如果使用之前的策略或者加下划线的命名策略,这里只要使用驼峰写法的,都会自动加下划线。我不想要这种命名策略,所以使用:PhysicalNamingStrategyStandardImpl,这样其实name可以省略了,但是为了规范我还是写上了。

    b),length  长度,比如String类型的属性 length写20,生成字段为varchar(20),这里注意:length的值要和后面写的columnDefinition中的例如:varchar(32)的值一致,不然启动时会报错。所以如果配置columnDefinition,建议可以不写length。

    c),nullable:能否为空  true:可以为空    false:不能为空。

这里其实还有一个属性:unique,唯一性约束,我这里发现一个问题;如果设置了unique,那么启动时会报错,但是启动能成功!而且,去表中看,唯一性约束设置成功。报错的大概意思好像是还没表无法设置唯一性约束。这里我觉得有可能涉及到底层原因。有时间再深究吧。但是我看着这个报错又难受,我就退而求其次,使用columnDefinition设置字段的唯一性约束,并且好处是还可以设置字段的备注,或者映射到表中的字段类型,以及长度。

    d),columnDefinition: 其实就是添加建表sql。例子代码都有。

9,@version  乐观锁,这个不是重点 不赘述了。

用户子类BaseUserEntity:

  1. 1
    package com.wm.springboot.base;
  2. 2
  3. 3
    import javax.persistence.Column;
  4. 4
    import javax.persistence.MappedSuperclass;
  5. 5
  6. 6
    import com.alibaba.fastjson.annotation.JSONField;
  7. 7
  8. 8
    import lombok.Getter;
  9. 9
    import lombok.Setter;
  10. 10
  11. 11
    @MappedSuperclass //表明这是父类,可以将属性映射到子类中使用JPA生成表
  12. 12
    public abstract class BaseUserEntity extends BaseEntity {
  13. 13
  14. 14
    @JSONField(ordinal=1)
  15. 15
    @Getter @Setter
  16. 16
    @Column(length=32,name="userName",nullable=false,columnDefinition="varchar(32) unique comment '用户名'")
  17. 17
    protected String userName; //用户名
  18. 18
  19. 19
    @JSONField(ordinal=2) @Getter @Setter
  20. 20
    @Column(length=32,name="password",nullable=false,columnDefinition="varchar(32) default '000000' comment '密码'")
  21. 21
    protected String password; //密码
  22. 22
  23. 23
    @JSONField(ordinal=3) @Getter @Setter
  24. 24
    @Column(length=64,name="email",nullable=false,columnDefinition="varchar(64) unique comment '邮箱'")
  25. 25
    protected String email; //邮箱号
  26. 26
  27. 27
    @JSONField(ordinal=4) @Getter @Setter
  28. 28
    @Column(length=11,name="phoneNo",nullable=false,columnDefinition="varchar(11) unique comment '电话号码'")
  29. 29
    protected String phoneNo; //手机号
  30. 30
  31. 31
    @JSONField(ordinal=5)
  32. 32
    @Getter @Setter
  33. 33
    @Column(length=32,name="realName",nullable=false,columnDefinition="varchar(32) comment '真实姓名'")
  34. 34
    protected String realName; //真实姓名
  35. 35
    }

注解参考上面解释。这么写的思路是,假设一个系统分管理用用户,和网站用户。这样用户也会有共同特性。所以再抽象一层。

WebUser类:

  1. 1
    package com.wm.springboot.sc.entity;
  2. 2
  3. 3
    import java.util.HashSet;
  4. 4
    import java.util.Set;
  5. 5
  6. 6
    import javax.persistence.CascadeType;
  7. 7
    import javax.persistence.Column;
  8. 8
    import javax.persistence.Entity;
  9. 9
    import javax.persistence.FetchType;
  10. 10
    import javax.persistence.JoinColumn;
  11. 11
    import javax.persistence.OneToMany;
  12. 12
  13. 13
    import com.alibaba.fastjson.JSONObject;
  14. 14
    import com.alibaba.fastjson.annotation.JSONField;
  15. 15
    import com.wm.springboot.base.BaseUserEntity;
  16. 16
  17. 17
    import lombok.AllArgsConstructor;
  18. 18
    import lombok.Getter;
  19. 19
    import lombok.NoArgsConstructor;
  20. 20
    import lombok.Setter;
  21. 21
  22. 22
    /**
  23. 23
    * 用户信息表
  24. 24
    * 原则:ID,用户名,邮箱号,手机号,微信ID都不可重复
  25. 25
    * @author maybe
  26. 26
    */
  27. 27
    @NoArgsConstructor
  28. 28
    @AllArgsConstructor
  29. 29
    /*
  30. 30
    * @Entity说明这是一个实体bean,使用orm默认规则(类名=表名;属性名=字段名)关联DB;
  31. 31
    * 如果想改变这种规则:1,可以配置@Entity的name来对应DB中的表名;@Entity(name="USER")
  32. 32
    * 2,使用@Table来改变class和DB表名的映射规则;@Column来改变属性名和字段名的映射规则
  33. 33
    */
  34. 34
    @Entity(name="WEBUSER")
  35. 35
    public class WebUser extends BaseUserEntity{
  36. 36
  37. 37
    @JSONField(ordinal=1) @Getter @Setter
  38. 38
    @Column(length=32,name="nickName",columnDefinition="varchar(32) comment '昵称'")
  39. 39
    private String nickName; //昵称
  40. 40
  41. 41
    @JSONField(ordinal=2) @Getter @Setter
  42. 42
    @Column(length=32,name="wxId",columnDefinition="varchar(32) unique comment '微信号'")
  43. 43
    private String wxId; //微信ID
  44. 44
  45. 45
    @JSONField(ordinal=3) @Getter @Setter
  46. 46
    @OneToMany(mappedBy="webUser",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
  47. 47
    private Set<Address> addresses;
  48. 48
  49. 49
    public WebUser(String username) {
  50. 50
    this.userName = username;
  51. 51
    }
  52. 52
  53. 53
    @Override
  54. 54
    public String toString() {
  55. 55
    return JSONObject.toJSONString(this,true);
  56. 56
    }
  57. 57
    }
@NoArgsConstructor  无参构造器

@AllArgsConstructor 全参构造器   不过只是本类的全部参数,如果需要使用父类参数,还需要自己写构造器。

@Entity(name="WEBUSER")  将被此注解标注的实体,映射到数据库,表名为name名。

@OneToMany(mappedBy="webUser",cascade=CascadeType.ALL,fetch=FetchType.LAZY)

    webuser是用户实体,一个用户对应多个地址,所以webuser是“一对多”中的“一”。在一的实体中,使用此注解标注。

mappedBy:标注该属性对应“多”的实体中的属性名。

cascade 表示级联操作。

fetch  加载方式,默认都是lazy加载。

重写toString方法,fastjson提供,将实体打印时,默认以json格式输出。  true的意思是标准json格式。


Address类:

  1. 1
    package com.wm.springboot.sc.entity;
  2. 2
  3. 3
    import javax.persistence.CascadeType;
  4. 4
    import javax.persistence.Column;
  5. 5
    import javax.persistence.Entity;
  6. 6
    import javax.persistence.FetchType;
  7. 7
    import javax.persistence.JoinColumn;
  8. 8
    import javax.persistence.ManyToOne;
  9. 9
  10. 10
    import com.alibaba.fastjson.JSONObject;
  11. 11
    import com.alibaba.fastjson.annotation.JSONField;
  12. 12
    import com.wm.springboot.base.BaseEntity;
  13. 13
  14. 14
    import lombok.Getter;
  15. 15
    import lombok.Setter;
  16. 16
  17. 17
    @Entity(name="ADDRESS")
  18. 18
    public class Address extends BaseEntity {
  19. 19
  20. 20
    @JSONField(ordinal=1) @Getter @Setter
  21. 21
    @Column(name="label",nullable=false,columnDefinition="varchar(16) comment '地址标签(家、公司)'")
  22. 22
    private String label;
  23. 23
  24. 24
    @JSONField(ordinal=2) @Getter @Setter
  25. 25
    @Column(name="country",nullable=false,columnDefinition="varchar(16) comment '国家'")
  26. 26
    private String country;
  27. 27
  28. 28
    @JSONField(ordinal=3) @Getter @Setter
  29. 29
    @Column(name="province",nullable=false,columnDefinition="varchar(32) comment '省份'")
  30. 30
    private String province;
  31. 31
  32. 32
    @JSONField(ordinal=4) @Getter @Setter
  33. 33
    @Column(name="city",nullable=false,columnDefinition="varchar(32) comment '城市'")
  34. 34
    private String city;
  35. 35
  36. 36
    @JSONField(ordinal=5) @Getter @Setter
  37. 37
    @Column(name="address",nullable=false,columnDefinition="varchar(255) comment '具体地址'")
  38. 38
    private String address;
  39. 39
  40. 40
    @JSONField(ordinal=6) @Getter @Setter
  41. 41
    @ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY,optional=true)
  42. 42
    @JoinColumn(name="webUser_id",nullable=true)
  43. 43
    private WebUser webUser;
  44. 44
  45. 45
    @Override
  46. 46
    public String toString() {
  47. 47
    return JSONObject.toJSONString(this,true);
  48. 48
    }
  49. 49
  50. 50
    }

@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY,optional=true)

address表为“一对多”中的多,所以使用@ManyToOne注解,并且配合@JoinColumn注解使用。

如果单独使用@ManyToOne,那么会生成一张中间表来维护两张表关系,如果不想使用中间表使用@JoinColumn来生成外键维护两张表关系。

name="webUser_id",表示生成的外键名称,并且字段类型以webUser表的主键为准。


多表联查的重点是:@ManyToOne@JoinColumn@OneToMany注解的使用。

==================================================

实体搞完了,下面搞一下repository层

比较简单了,普通的增删改JPA封装的很好了。这里重点说多条件查询及多表联查。先上代码。

WebUserRepository

  1. 1
    package com.wm.springboot.sc.repository;
  2. 2
  3. 3
    import org.springframework.data.domain.Page;
  4. 4
    import org.springframework.data.domain.Pageable;
  5. 5
    import org.springframework.data.jpa.domain.Specification;
  6. 6
    import org.springframework.data.jpa.repository.JpaRepository;
  7. 7
    import org.springframework.stereotype.Repository;
  8. 8
  9. 9
    import com.wm.springboot.sc.entity.WebUser;
  10. 10
  11. 11
    @Repository
  12. 12
    public interface WebUserRepository extends JpaRepository<WebUser, Integer>{
  13. 13
  14. 14
    public Page<WebUser> findAll(Specification<WebUser> specification,Pageable pageable);
  15. 15
    }

使用Specification来进行复杂条件查询,还可以使用Pageable进行分页查询。具体实现我们再service进行实现。

AddressRepository

  1. 1
    package com.wm.springboot.sc.repository;
  2. 2
  3. 3
    import org.springframework.data.domain.Page;
  4. 4
    import org.springframework.data.domain.Pageable;
  5. 5
    import org.springframework.data.jpa.domain.Specification;
  6. 6
    import org.springframework.data.jpa.repository.JpaRepository;
  7. 7
    import org.springframework.stereotype.Repository;
  8. 8
  9. 9
    import com.wm.springboot.sc.entity.Address;
  10. 10
  11. 11
    @Repository
  12. 12
    public interface AddressRepository extends JpaRepository<Address, Integer> {
  13. 13
  14. 14
    public Page<Address> findAll(Specification<Address> specification,Pageable pageable);
  15. 15
    }

=====================================

接下来是service层及repository的方法实现

面向接口变成我们先定义一下webuser的service

WebUserService

  1. 1
    package com.wm.springboot.sc.service;
  2. 2
  3. 3
    import org.springframework.data.domain.Page;
  4. 4
    import org.springframework.data.domain.Pageable;
  5. 5
  6. 6
    import com.wm.springboot.sc.entity.WebUser;
  7. 7
  8. 8
    public interface WebUserService {
  9. 9
  10. 10
    /**
  11. 11
    * 单表条件查询
  12. 12
    */
  13. 13
    public Page<WebUser> findAll(WebUser webUser,Pageable pageable);
  14. 14
  15. 15
    /**
  16. 16
    * 批量添加
  17. 17
    * @param list
  18. 18
    * @return
  19. 19
    */
  20. 20
    public WebUser save(WebUser webUser);
  21. 21
  22. 22
    /**
  23. 23
    * 单个删除
  24. 24
    * @param user
  25. 25
    */
  26. 26
    public void deleteOne(int id);
  27. 27
  28. 28
    /**
  29. 29
    * 单个修改
  30. 30
    */
  31. 31
    public WebUser update(WebUser webUser);
  32. 32
  33. 33
    /**
  34. 34
    * 根据ID查找
  35. 35
    */
  36. 36
    public WebUser findOne(int id);
  37. 37
    }

实现类WebUserServiceImpl

  1. 1
    package com.wm.springboot.sc.service.impl;
  2. 2
  3. 3
    import java.text.ParseException;
  4. 4
    import java.text.SimpleDateFormat;
  5. 5
    import java.util.ArrayList;
  6. 6
    import java.util.Date;
  7. 7
    import java.util.List;
  8. 8
  9. 9
    import javax.persistence.criteria.CriteriaBuilder;
  10. 10
    import javax.persistence.criteria.CriteriaQuery;
  11. 11
    import javax.persistence.criteria.Predicate;
  12. 12
    import javax.persistence.criteria.Root;
  13. 13
    import javax.transaction.Transactional;
  14. 14
  15. 15
    import org.springframework.beans.factory.annotation.Autowired;
  16. 16
    import org.springframework.data.domain.Page;
  17. 17
    import org.springframework.data.domain.Pageable;
  18. 18
    import org.springframework.stereotype.Service;
  19. 19
  20. 20
    import com.wm.springboot.sc.entity.WebUser;
  21. 21
    import com.wm.springboot.sc.repository.WebUserRepository;
  22. 22
    import com.wm.springboot.sc.service.WebUserService;
  23. 23
  24. 24
    @Service("WebUserServiceImpl")
  25. 25
    public class WebUserServiceImpl implements WebUserService {
  26. 26
  27. 27
    @Autowired
  28. 28
    private WebUserRepository webUserRepository;
  29. 29
  30. 30
    @Override
  31. 31
    public Page<WebUser> findAll(WebUser webUser, Pageable pageable) {
  32. 32
    Page<WebUser> page = webUserRepository
  33. 33
    .findAll((Root<WebUser> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
  34. 34
    List<Predicate> predicates = new ArrayList<Predicate>();
  35. 35
    predicates.add(cb.like(root.get("userName").as(String.class), "%"+webUser.getUserName() + "%"));
  36. 36
    predicates.add(cb.like(root.get("email").as(String.class), "%"+webUser.getEmail() + "%"));
  37. 37
    predicates.add(cb.like(root.get("phoneNo").as(String.class), "%"+webUser.getPhoneNo() + "%"));
  38. 38
    predicates.add(cb.equal(root.get("isValid").as(String.class), webUser.getIsValid()));
  39. 39
  40. 40
    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
  41. 41
    try {
  42. 42
    if (null != webUser.getCreateTimeStart() && !"".equals(webUser.getCreateTimeStart()))
  43. 43
    predicates.add(cb.greaterThanOrEqualTo(root.get("createTime").as(Date.class),
  44. 44
    f.parse(webUser.getCreateTimeStart())));
  45. 45
    if (null != webUser.getCreateTimeEnd() && !"".equals(webUser.getCreateTimeEnd()))
  46. 46
    predicates.add(cb.lessThan(root.get("createTime").as(Date.class),
  47. 47
    new Date(f.parse(webUser.getCreateTimeEnd()).getTime() + 24 * 3600 * 1000)));
  48. 48
    } catch (ParseException e) {
  49. 49
    e.printStackTrace();
  50. 50
    }
  51. 51
    return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
  52. 52
    }, pageable);
  53. 53
    return page;
  54. 54
  55. 55
    }
  56. 56
  57. 57
    @Override
  58. 58
    @Transactional
  59. 59
    public WebUser save(WebUser webUser) {
  60. 60
    return webUserRepository.save(webUser);
  61. 61
    }
  62. 62
  63. 63
    @Override
  64. 64
    public void deleteOne(int id) {
  65. 65
    webUserRepository.delete(id);
  66. 66
    }
  67. 67
  68. 68
    @Override
  69. 69
    public WebUser update(WebUser webUser) {
  70. 70
    return webUserRepository.save(webUser);
  71. 71
    }
  72. 72
  73. 73
    @Override
  74. 74
    public WebUser findOne(int id) {
  75. 75
    return webUserRepository.findOne(id);
  76. 76
  77. 77
    }
  78. 78
    }

重点说一下实现的findAll方法。因为使用jdk8,进入接口Specification,发现是函数式接口,直接使用lambda表达时进行书写。关于lambda表达式:

lambda概念及实际使用举例

简述一下这段逻辑,有错误请指正:

进入相应的方法可以看到:

root应该就是来获得字段的。

CriteriaBuilder 是用来拼装查询条件的。 如like  equal greaterThanOrEqualTo ......

将每一个Predicate添加到list,然后使用CriteriaQuery进行查询。

pageable,是用来分页查询的。


AddressService

  1. 1
    package com.wm.springboot.sc.service;
  2. 2
  3. 3
    import org.springframework.data.domain.Page;
  4. 4
    import org.springframework.data.domain.Pageable;
  5. 5
  6. 6
    import com.wm.springboot.sc.entity.Address;
  7. 7
  8. 8
    public interface AddressService {
  9. 9
  10. 10
    public Address save(Address address);
  11. 11
  12. 12
    public Page<Address> findAll(Pageable pageable,Address address);
  13. 13
    }

实现类AddressServiceImpl

  1. 1
    package com.wm.springboot.sc.service.impl;
  2. 2
  3. 3
    import java.util.ArrayList;
  4. 4
    import java.util.List;
  5. 5
  6. 6
    import javax.persistence.criteria.CriteriaBuilder;
  7. 7
    import javax.persistence.criteria.CriteriaQuery;
  8. 8
    import javax.persistence.criteria.Predicate;
  9. 9
    import javax.persistence.criteria.Root;
  10. 10
  11. 11
    import org.springframework.beans.factory.annotation.Autowired;
  12. 12
    import org.springframework.data.domain.Page;
  13. 13
    import org.springframework.data.domain.Pageable;
  14. 14
    import org.springframework.stereotype.Service;
  15. 15
  16. 16
    import com.wm.springboot.sc.entity.Address;
  17. 17
    import com.wm.springboot.sc.entity.WebUser;
  18. 18
    import com.wm.springboot.sc.repository.AddressRepository;
  19. 19
    import com.wm.springboot.sc.service.AddressService;
  20. 20
  21. 21
    @Service
  22. 22
    public class AddressServiceImpl implements AddressService {
  23. 23
  24. 24
    @Autowired
  25. 25
    private AddressRepository addressRepository;
  26. 26
  27. 27
    @Override
  28. 28
    public Address save(Address address) {
  29. 29
    return addressRepository.save(address);
  30. 30
    }
  31. 31
  32. 32
    @Override
  33. 33
    public Page<Address> findAll(Pageable pageable,Address address) {
  34. 34
    return addressRepository.findAll((Root<Address> root, CriteriaQuery<?> query, CriteriaBuilder cb)->{
  35. 35
    List<Predicate> predicates = new ArrayList<Predicate>();
  36. 36
    if(null!=address.getId()&&!"".equals(address.getId()))
  37. 37
    predicates.add(cb.equal(root.get("id").as(Integer.class),address.getId()));
  38. 38
    if(null!=address.getWebUser()&&!"".equals(address.getWebUser()))
  39. 39
    predicates.add(cb.equal(root.<WebUser>get("webUser").<Integer>get("id"),address.getWebUser().getId()));
  40. 40
    return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
  41. 41
    },pageable);
  42. 42
    }
  43. 43
  44. 44
    }
重点:root.<WebUser>get("webUser").<Integer>get("id")  通过获取address类中的属性webUser,得到一个webUser实体中的id,这个就是address中的外键。也是多表联查时的关键。

=======================================

controller层,访问控制器


先看一下webuserController

  1. 1
    package com.wm.springboot.sc.controller;
  2. 2
  3. 3
    import java.util.Date;
  4. 4
  5. 5
    import org.springframework.beans.factory.annotation.Autowired;
  6. 6
    import org.springframework.data.domain.Page;
  7. 7
    import org.springframework.web.bind.annotation.PostMapping;
  8. 8
    import org.springframework.web.bind.annotation.RequestMapping;
  9. 9
    import org.springframework.web.bind.annotation.RequestMethod;
  10. 10
    import org.springframework.web.bind.annotation.RestController;
  11. 11
  12. 12
    import com.wm.springboot.modelUtils.Pages;
  13. 13
    import com.wm.springboot.modelUtils.PagesUtils;
  14. 14
    import com.wm.springboot.modelUtils.RespResult;
  15. 15
    import com.wm.springboot.modelUtils.RespResultEnum;
  16. 16
    import com.wm.springboot.modelUtils.RespResultUtil;
  17. 17
    import com.wm.springboot.sc.entity.WebUser;
  18. 18
    import com.wm.springboot.sc.service.WebUserService;
  19. 19
  20. 20
    /**
  21. 21
    * 网站用户控制器
  22. 22
    * @author maybe
  23. 23
    */
  24. 24
    @RequestMapping("/WebUser")
  25. 25
    @RestController
  26. 26
    public class WebUserController {
  27. 27
  28. 28
    @Autowired
  29. 29
    private WebUserService webUserService;
  30. 30
  31. 31
    /**
  32. 32
    * 分页查询所有用户(动态页数,每页大小,排序方式,排序字段)
  33. 33
    * 包括动态条件查询(用户名,email,电话,是否启用,创建时间)
  34. 34
    * 规则:无输入条件,默认查询全部。默认返回第一页 每页5条,默认asc排序,默认id排序。
  35. 35
    */
  36. 36
    @RequestMapping(value="/findAll.do",method={RequestMethod.POST,RequestMethod.GET})
  37. 37
    public RespResult<Page<WebUser>> findAll(Pages pages,WebUser webUser){
  38. 38
    return RespResultUtil.success(webUserService.findAll(webUser, PagesUtils.createPageRequest(pages)));
  39. 39
    }
  40. 40
  41. 41
    /**
  42. 42
    * 添加
  43. 43
    */
  44. 44
    @PostMapping(value="/save.do")
  45. 45
    public RespResult<?> save(WebUser webUser){
  46. 46
    webUser.setCreateTime(new Date());
  47. 47
    webUser.setIsValid(1);
  48. 48
    webUser.setVersion(2);
  49. 49
    System.out.println(webUser.toString());
  50. 50
    WebUser webUser2 = webUserService.save(webUser);
  51. 51
    if(webUser2!=null) return RespResultUtil.success();
  52. 52
    else return RespResultUtil.error(RespResultEnum.ERROR);
  53. 53
    }
  54. 54
  55. 55
    /**
  56. 56
    * 单个删除
  57. 57
    */
  58. 58
    @RequestMapping(value="/deleteOne.do",method= {RequestMethod.POST,RequestMethod.GET})
  59. 59
    public RespResult<?> deleteOne(String id){
  60. 60
    try {
  61. 61
    webUserService.deleteOne(Integer.parseInt(id));
  62. 62
    } catch (Exception e) {
  63. 63
    return RespResultUtil.error(RespResultEnum.ERROR);
  64. 64
    }
  65. 65
    return RespResultUtil.success();
  66. 66
    }
  67. 67
  68. 68
    /**
  69. 69
    * 修改
  70. 70
    * @param webUser
  71. 71
    * @return
  72. 72
    */
  73. 73
    @RequestMapping(value="/update.do",method= {RequestMethod.POST,RequestMethod.GET})
  74. 74
    public RespResult<?> update(WebUser webUser){
  75. 75
    webUser.setVersion(webUserService.findOne(webUser.getId()).getVersion());
  76. 76
    System.out.println(webUser.toString());
  77. 77
    WebUser user = webUserService.update(webUser);
  78. 78
    if(user!=null) return RespResultUtil.success();
  79. 79
    else return RespResultUtil.error(RespResultEnum.ERROR);
  80. 80
    }
  81. 81
    }

RespResult、RespResultUtil为我自己封装的返回实体类。具体可参考:

哦,我还没来得及写。。。有空补上。

在说下Pages、PagesUtils类,这个也是我自己封装的分页相关的类:

pages类:

  1. 1
    package com.wm.springboot.modelUtils;
  2. 2
  3. 3
    import com.alibaba.fastjson.JSON;
  4. 4
  5. 5
    import lombok.AllArgsConstructor;
  6. 6
    import lombok.Getter;
  7. 7
    import lombok.NoArgsConstructor;
  8. 8
    import lombok.Setter;
  9. 9
  10. 10
    @AllArgsConstructor
  11. 11
    @NoArgsConstructor
  12. 12
    public class Pages {
  13. 13
  14. 14
    @Getter @Setter
  15. 15
    private int page;//第几页
  16. 16
    @Getter @Setter
  17. 17
    private int size;//每页显示几条内容
  18. 18
    @Getter @Setter
  19. 19
    private String sortColumn; //排序字段
  20. 20
    @Getter @Setter
  21. 21
    private String direction; //排序方式
  22. 22
  23. 23
    @Override
  24. 24
    public String toString() {
  25. 25
    return JSON.toJSONString(this, true);
  26. 26
    }
  27. 27
    }

PagesUtils类

  1. 1
    package com.wm.springboot.modelUtils;
  2. 2
  3. 3
    import org.springframework.data.domain.PageRequest;
  4. 4
    import org.springframework.data.domain.Pageable;
  5. 5
    import org.springframework.data.domain.Sort;
  6. 6
    import org.springframework.data.domain.Sort.Direction;
  7. 7
    import org.springframework.util.StringUtils;
  8. 8
  9. 9
    public class PagesUtils {
  10. 10
  11. 11
    //分页大小
  12. 12
    private final static Integer SIZE = 5;
  13. 13
    //默认页数 0开头
  14. 14
    private final static Integer PAGE = 0;
  15. 15
    //默认排序字段
  16. 16
    private final static String ID = "id";
  17. 17
  18. 18
    public static Pageable createPageRequest(Pages pages) {
  19. 19
    return new PageRequest(pages.getPage()<=0?PAGE:pages.getPage(),
  20. 20
    pages.getSize()<=0?SIZE:pages.getSize(),
  21. 21
    new Sort(null!=pages.getDirection()&&!"".equals(pages.getDirection())&&pages.getDirection().equals("desc")?Direction.DESC:Direction.ASC,
  22. 22
    StringUtils.isEmpty(pages.getSortColumn())?ID:pages.getSortColumn()));
  23. 23
    }
  24. 24
    }

这样的好处时,前台将页数,分页大小,排序字段,排序方式都从前台传入。灵活多变。还有一个没来得及补全的就是多字段排序。这个回来再添加。


然后是AddressController

  1. 1
    package com.wm.springboot.sc.controller;
  2. 2
  3. 3
    import org.springframework.beans.factory.annotation.Autowired;
  4. 4
    import org.springframework.web.bind.annotation.RequestMapping;
  5. 5
    import org.springframework.web.bind.annotation.RestController;
  6. 6
  7. 7
    import com.wm.springboot.modelUtils.Pages;
  8. 8
    import com.wm.springboot.modelUtils.PagesUtils;
  9. 9
    import com.wm.springboot.modelUtils.RespResult;
  10. 10
    import com.wm.springboot.modelUtils.RespResultEnum;
  11. 11
    import com.wm.springboot.modelUtils.RespResultUtil;
  12. 12
    import com.wm.springboot.sc.entity.Address;
  13. 13
    import com.wm.springboot.sc.service.AddressService;
  14. 14
  15. 15
    @RestController
  16. 16
    @RequestMapping(value="/address")
  17. 17
    public class AddressController {
  18. 18
  19. 19
    @Autowired
  20. 20
    private AddressService addressService;
  21. 21
  22. 22
  23. 23
    @RequestMapping(value="/save.do")
  24. 24
    public RespResult<?> save(Address address){
  25. 25
    address.setVersion(0);
  26. 26
    address.setIsValid(1);
  27. 27
    Address address2 = addressService.save(address);
  28. 28
    System.out.println(address2);
  29. 29
    if(address2!=null) return RespResultUtil.success();
  30. 30
    return RespResultUtil.error(RespResultEnum.ERROR);
  31. 31
    }
  32. 32
  33. 33
    @RequestMapping(value="/findAll.do")
  34. 34
    public RespResult<?> findAll(Address address,Pages pages){
  35. 35
    System.out.println(address.toString());
  36. 36
    return RespResultUtil.success(addressService.findAll(PagesUtils.createPageRequest(pages), address));
  37. 37
    }
  38. 38
  39. 39
    }
================================================================================================================
到此,java部分已经完成。其实后面可以使用junit测试一下。但是我junit使用的不是很好,总感觉用的不是特别舒服。

还有就是前台代码也可以稍微熟悉一下。所以下面看一下前台,使用html+jq实现。

直接上代码吧,一目了然

  1. 1
    <!DOCTYPE html>
  2. 2
    <html xmlns="http://www.w3.org/1999/xhtml"
  3. 3
    xmlns:th="http://www.thymeleaf.org">
  4. 4
    <head>
  5. 5
    <meta charset="UTF-8"/>
  6. 6
    <script type="text/javascript" src="/webjars/jquery/jquery.js"></script>
  7. 7
    <script type="text/javascript" src="/js/test.js"></script>
  8. 8
    </head>
  9. 9
    <body>
  10. 10
    <div>
  11. 11
    <p>id修改使用:<input type="text" name="id" id="id"/></p>
  12. 12
    <p>userName不能重复:<input type="text" name="userName" id="userName"/></p>
  13. 13
    <p>password:<input type="text" name="password" id="password"/></p>
  14. 14
    <p>email不能重复:<input type="text" name="email" id="email"/></p>
  15. 15
    <p>phoneNo不能重复:<input type="text" name="phoneNo" id="phoneNo"/></p>
  16. 16
    <p>realName:<input type="text" name="realName" id="realName"/></p>
  17. 17
    <input type="button" value="提交" id="submit"/>
  18. 18
    <input type="button" value="修改" id="update"/>
  19. 19
    </div>
  20. 20
    <div>
  21. 21
    <form>
  22. 22
    <p>label:<input type="text" name="label" id="label"/></p>
  23. 23
    <p>country:<input type="text" name="country" id="country"/></p>
  24. 24
    <p>province:<input type="text" name="province" id="province"/></p>
  25. 25
    <p>city:<input type="text" name="city" id="city"/></p>
  26. 26
    <p>address:<input type="text" name="address" id="address"/></p>
  27. 27
    <input type="button" value="提交address" id="submitAddress"/>
  28. 28
    </form>
  29. 29
    </div>
  30. 30
  31. 31
    <!-- <div> -->
  32. 32
    <!-- <form> -->
  33. 33
    <!-- <p>sender:<input type="text" name="sender" id="sender"/></p> -->
  34. 34
    <!-- <p>addressee:<input type="text" name="addressee" id="addressee"/></p> -->
  35. 35
    <!-- <p>msgTitle:<input type="text" name="msgTitle" id="msgTitle"/></p> -->
  36. 36
    <!-- <p>msgText:<input type="text" name="msgText" id="msgText"/></p> -->
  37. 37
    <!-- <input type="button" value="提交email" id="submitEmails"/> -->
  38. 38
    <!-- </form> -->
  39. 39
    <!-- </div> -->
  40. 40
  41. 41
    <div>
  42. 42
    <input type="button" value="查询所有" id="findAll"/>
  43. 43
    <input type="text" value="0" placeholder="页数" id="page" name="page"/>
  44. 44
    <input type="text" value="15" placeholder="大小" id="size" name="size"/>
  45. 45
    <input type="text" value="id" placeholder="排序列" id="sortColumn" name="sortColumn"/>
  46. 46
    <input type="text" value="asc" placeholder="排序方式" id="direction" name="direction"/>
  47. 47
    <input type="text" value="1" placeholder="是否启用" id="isValid" name="isValid"/>
  48. 48
    <input type="date" value="" placeholder="开始时间" id="createTimeStart" name="createTimeStart"/>
  49. 49
    <input type="date" value="" placeholder="结束时间" id="createTimeEnd" name="createTimeEnd"/>
  50. 50
    <input type="text" value="" placeholder="地址" id="addresses" name="addresses"/>
  51. 51
    </div>
  52. 52
  53. 53
    <h3>测试多表联查</h3>
  54. 54
    <div>
  55. 55
    关联ID:<input type="text" value="11" name="webUser" id="webUser"/>
  56. 56
    <input type="button" value="查询所有2" id="findAllAddress"/>
  57. 57
    </div>
  58. 58
  59. 59
    <div>
  60. 60
    <table>
  61. 61
    <thead>
  62. 62
    <tr>
  63. 63
    <td>ID</td>
  64. 64
    <td>USERNAME</td>
  65. 65
    <td>EMAIL</td>
  66. 66
    <td>PHONENO</td>
  67. 67
    <td>REALNAME</td>
  68. 68
    <td>DELETE</td>
  69. 69
    </tr>
  70. 70
    </thead>
  71. 71
    <tbody id="content">
  72. 72
    </tbody>
  73. 73
    </table>
  74. 74
    </div>
  75. 75
    </body>
  76. 76
    <script type="text/javascript">
  77. 77
  78. 78
    $(function(){
  79. 79
  80. 80
    $("#findAllAddress").click(function(){
  81. 81
    alert("123");
  82. 82
    $.post("/address/findAll.do",{
  83. 83
    webUser:$("#webUser").val(),
  84. 84
    id:$("#id").val()
  85. 85
    },function(data){
  86. 86
    alert(data.data.content);
  87. 87
    });
  88. 88
    });
  89. 89
  90. 90
    $("#submit").on('click', function() {
  91. 91
  92. 92
    var _userName = $("#userName").val();
  93. 93
    var _password = $("#password").val();
  94. 94
    var _email = $("#email").val();
  95. 95
    var _phoneNo = $("#phoneNo").val();
  96. 96
    var _realName = $("#realName").val();
  97. 97
  98. 98
    $.post("/WebUser/save.do", {
  99. 99
    userName : _userName,
  100. 100
    password : _password,
  101. 101
    email : _email,
  102. 102
    phoneNo : _phoneNo,
  103. 103
    realName : _realName
  104. 104
    }, function(data) {
  105. 105
    alert(data.msg);
  106. 106
    });
  107. 107
    });
  108. 108
  109. 109
  110. 110
    $("#submitAddress").on('click', function() {
  111. 111
  112. 112
    var _label = $("#label").val();
  113. 113
    var _country = $("#country").val();
  114. 114
    var _province = $("#province").val();
  115. 115
    var _city = $("#city").val();
  116. 116
    var _address = $("#address").val();
  117. 117
  118. 118
    $.post("/address/save.do", {
  119. 119
    label : _label,
  120. 120
    country : _country,
  121. 121
    province : _province,
  122. 122
    city : _city,
  123. 123
    address : _address
  124. 124
    }, function(data) {
  125. 125
    alert(data.msg);
  126. 126
    });
  127. 127
    });
  128. 128
  129. 129
    $("#update").on('click', function() {
  130. 130
  131. 131
    var _id = $("#id").val();
  132. 132
    var _userName = $("#userName").val();
  133. 133
    var _password = $("#password").val();
  134. 134
    var _email = $("#email").val();
  135. 135
    var _phoneNo = $("#phoneNo").val();
  136. 136
    var _realName = $("#realName").val();
  137. 137
  138. 138
    $.post("/WebUser/update.do", {
  139. 139
    id : _id,
  140. 140
    userName : _userName,
  141. 141
    password : _password,
  142. 142
    email : _email,
  143. 143
    phoneNo : _phoneNo,
  144. 144
    realName : _realName
  145. 145
    }, function(data) {
  146. 146
    alert(data.msg);
  147. 147
    });
  148. 148
    });
  149. 149
  150. 150
    $("#findAll").on(
  151. 151
    'click',
  152. 152
    function() {
  153. 153
    $.post("/WebUser/findAll.do", {
  154. 154
    page : $("#page").val(),
  155. 155
    size : $("#size").val(),
  156. 156
    sortColumn : $("#sortColumn").val(),
  157. 157
    direction : $("#direction").val(),
  158. 158
    userName : $("#userName").val(),
  159. 159
    email : $("#email").val(),
  160. 160
    phoneNo : $("#phoneNo").val(),
  161. 161
    isValid : $("#isValid").val(),
  162. 162
    createTimeStart : $("#createTimeStart").val(),
  163. 163
    createTimeEnd : $("#createTimeEnd").val()
  164. 164
    }, function(data) {
  165. 165
    $("#content").html();
  166. 166
    var html = "";
  167. 167
    $.each(data.data.content, function(i, val) {
  168. 168
    html += "<tr><td>" + val.id + "</td>" + "<td>"
  169. 169
    + val.userName + "</td>" + "<td>" + val.email
  170. 170
    + "</td>" + "<td>" + val.phoneNo + "</td>"
  171. 171
    + "<td>" + val.realName + "</td>"
  172. 172
    + '<td><a href="/WebUser/deleteOne.do?id='
  173. 173
    + val.id + '">删除</a></td></tr>';
  174. 174
    });
  175. 175
    $("#content").html(html);
  176. 176
    });
  177. 177
    });
  178. 178
  179. 179
    })
  180. 180
  181. 181
  182. 182
    </script>
  183. 183
    </html>


启动项目:

看到数据库生成的表:


可以看到父类的字段,备注等都有。


随手添加了几条数据。

然后测试两个表的findAll是否可以成功的进行关联查询及webuser的复杂条件查询。

测试结果:

复杂条件查询:




多表联查:

{
	"code":0,
	"msg":"处理成功",
	"remindMsg":"处理成功",
	"data":{
		"content":[
			{
				"id":1,
				"label":"1",
				"country":"1",
				"province":"1",
				"city":"1",
				"version":0,
				"address":"1",
				"isValid":1,
				"webUser":{
					"id":11,
					"userName":"123",
					"createTime":"2018-06-17 21:06:26",
					"password":"123",
					"addresses":[{
						"id":3,
						"label":"3",
						"country":"3",
						"province":"3",
						"city":"3",
						"version":0,
						"address":"3",
						"isValid":1,
						"webUser":{"$ref":"$.data.content[0].webUser"}
					},{
						"id":2,
						"label":"2",
						"country":"2",
						"province":"2",
						"city":"2",
						"version":0,
						"address":"2",
						"isValid":1,
						"webUser":{"$ref":"$.data.content[0].webUser"}
					},{"$ref":"$.data.content[0]"}],
					"email":"222",
					"phoneNo":"123",
					"version":2,
					"isValid":1,
					"realName":"123"
				}
			},
			{"$ref":"$.data.content[0].webUser.addresses[1]"},
			{"$ref":"$.data.content[0].webUser.addresses[0]"}
		],
		"first":true,
		"last":true,
		"number":0,
		"numberOfElements":3,
		"size":5,
		"sort":[{
			"ascending":true,
			"descending":false,
			"direction":"ASC",
			"ignoreCase":false,
			"nullHandling":"NATIVE",
			"property":"id"
		}],
		"totalElements":3,
		"totalPages":1
	}
}


吴泾出了一位股市怪才,炒股2年他只靠1不败铁律 渝和 · 鹓鶵
吴泾25岁美女手机做这个,1年存款吓呆父母!! 羿尚 · 鹓鶵
张小凡vip关注
张小凡vip

828篇文章

排名:139

_bone_关注
_bone_

51篇文章

排名:千里之外

laokaizzz关注
laokaizzz

597篇文章

排名:2000+

追逐盛夏流年关注
追逐盛夏流年

16篇文章

排名:千里之外

吴泾25岁美女手机做这个,1年存款吓呆父母!! 赛尔网络科技 · 鹓鶵
吴泾出了一位股市怪才,炒股2年他只靠1不败铁律 渝和 · 鹓鶵
千万不要再乱喝蜂蜜了,记者亲赴深山,揭露背后惊人黑幕! 树米 · 鹓鶵
一位退役操盘手肺腑之言,写给无数正在亏钱的散户! 豪嘉利 · 鹓鶵
一个长期喝土蜂蜜的人,竟然变成了这样,再忙也要看一下 科技 · 鹓鶵
原创
44
粉丝
12
喜欢
25
评论
33
等级:
访问:
3万+
积分:
779
排名:
8万+
勋章:

博主专栏

最新评论